import log from '../log';

const isDev = process.env.NODE_ENV !== 'production';

function done(ctx, body, code = 200) {
    ctx.status = code;
    if (body !== undefined &&
        body !== null) {
        ctx.body = body;
    } else {
        ctx.status = 204;
    }
}

export function fail(ctx, err) {
    log.error('error caught:', err);
    let status = 500;
    let message = '';
    let msg;
    switch (typeof err) {
        case 'number':
            status = err;
            break;
        case 'string':
            message = err;
            break;
        case 'object':
            switch (typeof err.code) {
                case 'number':
                    status = err.code;
                    break;
                case 'string':
                    if (err.code.startsWith('LIMIT_')) {
                        status = 413; // for multer
                    }
                    break;
                default:
                    if (typeof err.statusCode === 'number') {
                        status = err.statusCode;
                    } else if (typeof err.status === 'number') {
                        status = err.status;
                    }
                    break;
            }
            msg = err.message || err.text || err.toString();
            //if (isDev) message = msg;
            message = msg;
            if (err.code) {
                ctx.log.error(`${ctx.method} ${ctx.path} [${status}] ${msg}`);
            }
            break;
        default:
            break;
    }
    ctx.status = status;
    ctx.body = message;
    return { status, message };
}

/**
 * 为ctx添加done和fail方法
 */
export function ext(opts) {
    return async (ctx, next) => {
        const log = opts.log || console.error.bind(console);
        ctx.log = log;
        ctx.done = done.bind(undefined, ctx);
        ctx.fail = fail.bind(undefined, ctx);
        await next();
    };
}

/**
 * 通用的错误处理中间件
 * @param opts 选项（log - 日志）
 */
/* eslint no-console: 0 */
export function error(opts) {
    const log = opts.log || console.error.bind(console);

    return (ctx, next) => next().catch(err => {
        const { status, message } = fail(ctx, err);
        log.error(`${ctx.method} ${ctx.path} [${status}] ${message}`);
        if (isDev) console.error(err);
    });
}
